{
 "cells": [
  {
   "cell_type": "code",
   "source": [
    "# First initialize GsSession; external users should substitute client_id and client_secret fields with their credentials\n",
    "from gs_quant.session import GsSession, Environment\n",
    "import seaborn as sns\n",
    "\n",
    "GsSession.use(Environment.PROD, client_id=None, client_secret=None)\n",
    "\n",
    "# Global parameters for plots\n",
    "params = {'axes.axisbelow': False, 'axes.edgecolor': 'lightgrey', 'axes.grid': False, 'axes.labelcolor': 'dimgrey', 'axes.labelsize': 'large', 'axes.spines.right': False, 'axes.spines.top': False, 'axes.titlesize': 'x-large','axes.titleweight': 'bold',\n",
    "    'figure.dpi': 200, 'figure.facecolor': 'white', 'figure.figsize': (15, 7), 'legend.frameon': False, 'legend.fontsize': 'xx-small', 'lines.solid_capstyle': 'round', 'patch.edgecolor': 'w', 'patch.force_edgecolor': True, 'text.color': 'dimgrey',\n",
    "    'xtick.bottom': False, 'xtick.color': 'dimgrey', 'xtick.direction': 'out', 'xtick.labelsize': 'x-small', 'xtick.top': False, 'ytick.color': 'dimgrey', 'ytick.direction': 'out', 'ytick.left': False, 'ytick.right': False, 'ytick.labelsize': 'small'\n",
    "}\n",
    "\n",
    "exp_palette= ['#53aaea', '#67b4ec', '#7bbeef', '#8fc8f1', '#a4d2f4', '#b8dcf6', '#cbe6f9', '#dfeffb', '#fce4e1', '#fad6d2', '#f9c9c3', '#f7bbb4', '#f6ada5', '#f49f96', '#f39287', '#f18478']\n",
    "longer_exp_palette = [col for col in exp_palette for i in range(3)]\n",
    "sns.set(style='white', rc=params)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Discover your Macro risk with Quant Insight models\n",
    "\n",
    "Quant Insight's Macro risk models provide an insight in the relationship between movement in an asset price and\n",
    "macroeconomic factors such as economic growth, monetary policy and commodity prices. The goal is to understand how much\n",
    "of the movement in the asset price is attributable to those macroeconomic factors. The GS quant class `MacroRiskModel`\n",
    "provides an array of functions that query macro risk model data. In this tutorial, we will look at querying all the\n",
    "available macro risk model data.\n",
    "\n",
    "Currently, the macro risk models that are available for programmatic access are below:\n",
    "\n",
    "| Risk Model Name             | Risk Model Id |\n",
    "|-----------------------------|---------------|\n",
    "| US Equity Model (Long Term) | QI_US_EQUITY_LT |\n",
    "| EU Equity Model (Long Term) | QI_EU_EQUITY_LT |\n",
    "| UK Equity Model (Long Term) | QI_UK_EQUITY_LT |\n",
    "| APAC Equity Model (Long Term) | QI_APAC_EQUITY_LT |\n",
    "\n",
    "## Macro Factor Data\n",
    "\n",
    "Macro Factors are grouped within a factor category.\n",
    "Here are some examples of macro factor categories along with a list of macro factors in that category:\n",
    "\n",
    "| Macro Factor Category | Macro Factors                                              |\n",
    "|-----------------------|------------------------------------------------------------|\n",
    "| Inflation             | US 5Y Infl. Expec., US 2Y Infl. Expec., US 10Y Infl. Expec.|\n",
    "| Economic Growth       | Japan GDP, Euro GDP, China GDP                             |\n",
    "| CB Rate Expectations  | Fed Rate Expectations                                      |\n",
    "| Energy                | WTI                                                        |\n",
    "| Risk Aversion         | VDAX, VIX, VXEEM, Gold Silver Ratio                        |\n",
    "\n",
    "For more macro factor categories and their descriptions, see the factor glossary.\n",
    "\n",
    "#### Get All Available Macro Factor Categories\n",
    "\n",
    "We can leverage `get_factor_data` in the `MacroRiskModel` class to get all the available macro factor categories\n",
    "in the model.\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, FactorType\n",
    "import datetime as dt\n",
    "\n",
    "start_date = dt.date(2022, 4, 1)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Get the factors in dataframe with name, and type\n",
    "factor_category_data = model.get_factor_data(start_date=start_date, factor_type=FactorType.Factor)\n",
    "factor_category_data_reshaped = (\n",
    "    factor_category_data.set_index(\"factorCategoryId\")\n",
    "                        .drop(columns={\"name\", \"type\", \"identifier\"})\n",
    "                        .drop_duplicates()\n",
    "                        .rename_axis(\"Factor Category Id\")\n",
    "                        .rename(columns={\"factorCategory\": \"Factor Category\"})\n",
    ")\n",
    "display(factor_category_data_reshaped)\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Get All Available Macro Factors\n",
    "\n",
    "Within each macro factor category, we have several macro factors that are grouped together."
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, FactorType\n",
    "import datetime as dt\n",
    "import pandas as pd\n",
    "\n",
    "start_date = dt.date(2022, 4, 1)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Get the factors in dataframe with name, and type\n",
    "factor_data = model.get_factor_data(start_date=start_date, factor_type=FactorType.Factor)\n",
    "factor_data_reshaped = (\n",
    "    factor_data.rename(columns={\"name\": \"Factor\", \"factorCategory\": \"Factor Category\"})\n",
    "               .sort_values(by=[\"Factor Category\"])\n",
    "               .drop(columns={\"type\", \"factorCategoryId\", \"identifier\"})\n",
    "               .set_index(\"Factor Category\")\n",
    "               .stack().to_frame().droplevel(level=1).rename(columns={0: \"Factor\"})\n",
    "\n",
    ")\n",
    "display(factor_data_reshaped)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "Further, for a more granular view, we can get all the macro factors that are grouped within a factor category.\n",
    "Below is a list of all macro factors in the factorCategory \"Risk Aversion\"."
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "macro_factors_in_risk_aversion = factor_data_reshaped.groupby(\"Factor Category\").get_group(\"Risk Aversion\")\n",
    "display(macro_factors_in_risk_aversion)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Asset Data\n",
    "\n",
    "The metrics below are instrumental in providing insight in the relationship between movement of asset price and macro\n",
    "factors. These key metrics are outlined below:\n",
    "\n",
    "| Metric                    | Description    |\n",
    "|----------------------------|---------------|\n",
    "| `Universe Macro Factor Sensitivity` | Percentage change in asset price for 1 standard deviation move up in the macro factor. |\n",
    "| `R Squared (Model Confidence)`               | Gauge of how sensitive the asset is to macroeconomic forces.  Typically values above 65% are considered to show strong explanatory power or ‘confidence’. |\n",
    "| `Fair Value Gap`          | The difference between the actual price of an asset and the Qi Model Value.  This is quoted both in absolute (in percentage) and in standard deviation terms.  |\n",
    "| `Specific Risk`            | Annualized asset volatility. |\n",
    "\n",
    "\n",
    "## Get  your Portfolio Exposure to Macro Factors and Categories\n",
    "\n",
    "Given a portfolio, we can get its cash exposure to a 1 standard deviation move up in each macro factor. The exposures are\n",
    "calculated by multiplying each asset notional amount to their sensitivity to each factor, which gives us individual asset\n",
    " exposure to each macro factor. We then aggregate per asset exposures to get portfolio cash exposure to each macro \\\n",
    " factor.\n",
    "We can leverage the function `get_macro_exposure_table` of the `PortfolioManager` class. Note that exposure is expressed\n",
    "in the currency of the notional value of the portfolio.\n",
    "\n",
    "We can get portfolio exposure to both macro factor categories and macro factors. We can thus gain an insight\n",
    "in which factor categories and macro factors within these categories are driving the portfolio.\n",
    "\n",
    "#### Exposure to Macro Factor Categories\n",
    "\n",
    "We need to pass the following parameters to the function:\n",
    "* `macro_risk_model`: The model to base your exposure data on\n",
    "* `date`: date for which to get exposure\n",
    "* `group_by_factor_category`: whether to get exposure per factor category.\n",
    "\n",
    "The result will be sorted from top positive drivers to top negative drivers\n",
    "\n",
    "Note that, in the result, the macro factor categories are ranked from most positive exposure to most negative portfolio exposure\n",
    "\n",
    "Below:\n",
    "* We get asset level exposure to each factor category.\n",
    "* The last row of the resulting dataframe is the portfolio exposure to each factor category (asset level exposure aggregated)."
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, FactorType\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager\n",
    "from IPython.display import display\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import datetime as dt\n",
    "\n",
    "# Get the model\n",
    "model = MacroRiskModel.get(\"QI_US_EQUITY_LT\")\n",
    "\n",
    "# Input the portfolio id\n",
    "portfolio_id = \"MPE2KR4BC4RKQ5KE\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "date = dt.date(2022, 5, 2)\n",
    "\n",
    "# Get Per Asset Factor Category Exposure Dataframe\n",
    "exposure_factor_category_df = pm.get_macro_exposure(model=model, date=date, factor_type=FactorType.Category)\n",
    "exposure_factor_category_df.loc[\"Total Factor Category Exposure\", \"Asset Name\"] = \"Total\"\n",
    "exposure_factor_category_df = exposure_factor_category_df.set_index(\"Asset Name\", drop=True)\n",
    "\n",
    "# Get the Portfolio Total\n",
    "total = exposure_factor_category_df.loc[\"Total\", :].to_frame().drop(\"Notional\")\n",
    "\n",
    "# Display the Dataframes.\n",
    "styles = [{'selector': \"caption\", 'props': [(\"font-size\", \"200%\"), (\"font-weight\", \"bold\"), (\"color\", 'black')]}, {'selector': 'thead', 'props': [(\"background-color\", \"silver\"), (\"font-size\", \"13px\")]}, {'selector': 'tr', 'props': [(\"background-color\", \"whitesmoke\"), (\"font-size\", \"13px\"), (\"border\", \"solid\"), (\"border-width\", \"0.001em\")]},\n",
    "          {'selector': 'td', 'props': [(\"background-color\", \"aliceblue\"), (\"font-size\", \"13px\")]}]\n",
    "exposure_styler = (\n",
    "    exposure_factor_category_df.style.set_caption(\"Portfolio Cash Exposure to Factor Categories\")\n",
    "                                     .format('${:,.0f}')\n",
    "                                     .applymap(lambda v: \"color:red;\" if v < 0 else None)\n",
    "                                     .set_table_styles(styles)\n",
    ")\n",
    "styles.pop(0)\n",
    "total_styler = total.style.format('${:,.0f}').applymap(lambda v: \"color:red;\" if v < 0 else None).set_table_styles(styles)\n",
    "display(exposure_styler)\n",
    "display(total_styler)\n",
    "\n",
    "# Plot\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "sns.barplot(data=total.reset_index(), x=\"Factor Category\", y='Total', ax=ax, palette=exp_palette)\n",
    "ax.set_xlabel(\"Factor Category\")\n",
    "ax.set_ylabel(\"Portfolio Notional Change for 1 std Move in the Factor Category\", fontsize='medium')\n",
    "ax.set_title(\"Portfolio Exposure to Factor Categories\")\n",
    "ax.tick_params(axis='x', labelrotation = 50)\n",
    "plt.show()\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Exposure to Macro Factors\n",
    "Once we get portfolio exposure to factor categories, we can get a granular view of the portfolio exposure to individual macro factors within that factor category.\n",
    "\n",
    "Here are the user inputs:\n",
    "* `model`: The model we are using.\n",
    "* `factor_categories`: A list of factor categories whose factors we want exposure for. If empty, it will default to returning exposure to the top 2 positive and negative macro drivers\n",
    "* `portfolio_id`: The portfolio we are getting macro exposure for.\n",
    "* `date`: Date "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, FactorType\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager\n",
    "from IPython.display import display\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import datetime as dt\n",
    "\n",
    "# model id\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "portfolio_id = \"MPE2KR4BC4RKQ5KE\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "\n",
    "# date\n",
    "date = dt.date(2022, 5, 2)\n",
    "\n",
    "# Add Factor categories whose factors you want exposure for\n",
    "factor_categories = []\n",
    "factor_categories_data = model.get_many_factors(start_date=date, end_date=date, factor_names=factor_categories, factor_type=FactorType.Category)\n",
    "\n",
    "# Get Per Asset Factor Exposure dataframe\n",
    "exp_factor_df = pm.get_macro_exposure(model=model, date=date, factor_type=FactorType.Factor, factor_categories=factor_categories_data)\n",
    "exp_factor_df.loc[\"Total Factor Exposure\", (\"Asset Information\", \"Asset Name\")] = \"Total\"\n",
    "exp_factor_df = exp_factor_df.set_index((\"Asset Information\", \"Asset Name\"), drop=True)\n",
    "\n",
    "# Total Portfolio Exposure\n",
    "total = exp_factor_df.loc[\"Total\", :].to_frame().drop((\"Asset Information\", \"Notional\"))\n",
    "\n",
    "# Display the Dataframes.\n",
    "styles = [{'selector': \"caption\", 'props': [(\"font-size\", \"200%\"), (\"font-weight\", \"bold\"), (\"color\", 'black')]}, {'selector': 'thead', 'props': [(\"background-color\", \"silver\"), (\"font-size\", \"13px\")]}, {'selector': 'tr', 'props': [(\"background-color\", \"whitesmoke\"), (\"font-size\", \"13px\"), (\"border\", \"solid\"), (\"border-width\", \"0.001em\")]},\n",
    "          {'selector': 'td', 'props': [(\"background-color\", \"aliceblue\"), (\"font-size\", \"13px\")]}]\n",
    "exposure_styler = (\n",
    "    exp_factor_df.style.set_caption(\"Portfolio Cash Exposure to Macro Factors\")\n",
    "                       .format('${:,.0f}')\n",
    "                       .applymap(lambda v: \"color:red;\" if v < 0 else None).set_table_styles(styles)\n",
    ")\n",
    "styles.pop(0)\n",
    "total_styler = total.style.format('${:,.0f}').applymap(lambda v: \"color:red;\" if v < 0 else None).set_table_styles(styles)\n",
    "display(exposure_styler)\n",
    "display(total_styler)\n",
    "\n",
    "# Plot the portfolio percent change if every  factor category shifted 1 std\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "sns.barplot(data=total.sort_values(by=[\"Total\"], ascending=False).reset_index(), x=\"Factor\", y='Total', ax=ax, palette=longer_exp_palette)\n",
    "ax.set_xlabel(\"Macro Factor\")\n",
    "ax.set_ylabel(\"Portfolio Notional Change for 1 std Move in the Macro Factor\", fontsize='medium')\n",
    "ax.set_title(\"Portfolio Exposure to Macro Factors\")\n",
    "ax.tick_params(axis='x', labelrotation=60)\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "\n",
    "### Run empirical stress tests to observe impact on portfolio\n",
    "\n",
    "We can bump up or down any combination of macro factors and see the impact on the portfolio. Since your portfolio's\n",
    "sensitivities to each macro factor are independent of each other, we can isolate a (or a combination of) macro factor categories and factors,\n",
    "and assign a shift up or down in standard deviation and observe the change in the portfolio notional value.\n",
    "\n",
    "Below, we define a class `MacroScenario` that takes in a date and a dictionary of macro factor categories or macro factors and their suggested standard\n",
    "deviation shifts."
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.session import GsSession, Environment\n",
    "from gs_quant.models.risk_model import MacroRiskModel, FactorType\n",
    "from gs_quant.markets.factor import Factor\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager\n",
    "from typing import Dict\n",
    "import datetime as dt\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "GsSession.use(Environment.PROD, client_id=None, client_secret=None)\n",
    "\n",
    "class MacroScenario:\n",
    "    def __init__(self, date: dt.date, factor_std_shifts: Dict[Factor, float]):\n",
    "        self.date = date\n",
    "        self.factor_std_shifts = factor_std_shifts\n",
    "\n",
    "\n",
    "def macro_stress_test(exposure_df: pd.DataFrame,\n",
    "                      scenario: MacroScenario,\n",
    "                      is_factor_category_stress: bool) ->  pd.DataFrame:\n",
    "\n",
    "    \"\"\"\n",
    "    For a given portfolio, find how shifts in standard deviation in the selected macro factors affects\n",
    "    the portfolio's notional value.\n",
    "    :param exposure_df: exposure of the portfolio to the requested macro factors/factor categories\n",
    "    :param scenario: scenario to stress test for. Contains date and a dict of factors and respective shifts in standard\n",
    "    deviation\n",
    "    :is_factor_category_stress: whether we are stress testing for shifts in factor categories or shifts in factors.\n",
    "    :return: portfolio notional change and change in portfolio exposure for each macro factor\n",
    "    \"\"\"\n",
    "\n",
    "    factors_and_std = scenario.factor_std_shifts\n",
    "    factors_to_shift = {factor.name: factor.category for factor in factors_and_std.keys()}\n",
    "    total_row_name = \"Total Factor Category Exposure\" if is_factor_category_stress else \"Total Factor Exposure\"\n",
    "    notional_column_name = \"Notional\" if is_factor_category_stress else (\"Asset Information\", \"Notional\")\n",
    "    portfolio_notional = exposure_df.loc[total_row_name, notional_column_name]\n",
    "    exposure_df = exposure_df[list(factors_to_shift.keys())] if is_factor_category_stress else\\\n",
    "        exposure_df[list(zip(factors_to_shift.values(), factors_to_shift.keys()))]\n",
    "\n",
    "    fact_name = 'Factor Category' if is_factor_category_stress else 'Factor'\n",
    "    col_1_name = f\"Exposure to {fact_name} Before Stress Test\"\n",
    "    col_2_name = f\"Exposure to {fact_name} After Stress Test\"\n",
    "    portfolio_change_df = pd.DataFrame(np.zeros((4, len(set(exposure_df.columns.tolist())))),\n",
    "                                       index=[\"Standard Deviation Shift\", col_1_name, col_2_name,\n",
    "                                              \"Percent Change After Stress Test\"],\n",
    "                                       columns=set(exposure_df.columns.tolist()))\n",
    "\n",
    "    for factor, std_shift in factors_and_std.items():\n",
    "        factor_name = factor.name if is_factor_category_stress else (factors_to_shift[factor.name], factor.name)\n",
    "        old_exposure = exposure_df.loc[total_row_name, factor_name]\n",
    "        new_exposure = old_exposure * std_shift\n",
    "        portfolio_change_df.loc[\"Standard Deviation Shift\", factor_name] = std_shift\n",
    "        portfolio_change_df.loc[col_1_name, factor_name] = old_exposure\n",
    "        portfolio_change_df.loc[col_2_name, factor_name] = new_exposure\n",
    "        portfolio_change_df.loc[\"Percent Change After Stress Test\", factor_name] = (new_exposure / portfolio_notional) * 100\n",
    "\n",
    "    total_name = \"Total\" if is_factor_category_stress else (\"Total\", \"Total\")\n",
    "    total_sum = portfolio_change_df.agg(np.sum, axis=\"columns\").to_frame().rename(columns={0: total_name})\n",
    "\n",
    "    portfolio_change_df = pd.concat([portfolio_change_df, total_sum], axis='columns')\n",
    "    portfolio_change_df.loc[\"Standard Deviation Shift\", total_name] = np.NAN\n",
    "\n",
    "    return portfolio_change_df"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Factor Standard Deviation\n",
    "\n",
    "Before the stress test, we can first determine, for each factor, the value of one shift in standard deviation in absolute terms. We can leverage the function `get_factor_standard_deviation` of the `MacroRiskModel` class."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "import datetime as dt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "start_date = dt.date(2022, 1, 1)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "date = model.get_dates(start_date, end_date)[-1]\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "factor_category_shift_std = {\n",
    "   \"Inflation\": 3,\n",
    "   \"Real Rates\": 3,\n",
    "   \"Economic Growth\": -3\n",
    "}\n",
    "\n",
    "factor_data = model.get_factor_data(date, date)\n",
    "factor_data = factor_data.loc[factor_data['factorCategory'].isin(list(factor_category_shift_std.keys())), :]\n",
    "factor_data = factor_data.set_index(\"name\")\n",
    "\n",
    "factor_std_df = model.get_factor_standard_deviation(start_date=date, end_date=date, factors=factor_data.index.tolist())\n",
    "\n",
    "factor_std_df = (\n",
    "    factor_std_df.set_axis(pd.MultiIndex.from_tuples([(factor_data.loc[f, 'factorCategory'], f) for f in factor_std_df.columns.values]), axis=1)\n",
    "                 .rename_axis((\"Factor Category\", \"Factor\"), axis=1)\n",
    "                 .sort_index(level=0, axis=1)\n",
    "                 .round(3)\n",
    "                 .T.rename(columns={date.isoformat(): \"Value of 1 STD\"})\n",
    ")\n",
    "\n",
    "display((\n",
    "    factor_std_df.style.set_caption(f\"One Standard Deviation of Factors on {date.isoformat()}\")\n",
    "                       .format(\"{:.3f}\", na_rep=\"N/A\").background_gradient()\n",
    "                       .set_table_styles([dict(selector=\"caption\", props=[(\"font-size\", \"120%\"), (\"font-weight\", \"bold\"),(\"color\", 'black')])])\n",
    "))"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Stress Test Shifts in Factor Categories\n",
    "\n",
    "We can stress test for shifts in factor categories and observe the impact on our portfolio. \n",
    "\n",
    "Here are the user inputs:\n",
    "* `model_id`: The model we are using\n",
    "* `portfolio_id`: Portfolio we are stress testing for.\n",
    "* `factors_shifts_std`: Map of factor categories and their standard deviation scenario shift"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "import datetime as dt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "date = dt.date(2022, 5, 2)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "portfolio_id = \"MPE2KR4BC4RKQ5KE\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "\n",
    "factor_category_shift_std = {\n",
    "   \"Inflation\": 0.5,\n",
    "   \"Real Rates\": 3,\n",
    "   \"Economic Growth\": -2\n",
    "}\n",
    "many_factor_categories = model.get_many_factors(start_date=date, end_date=date,\n",
    "                                                factor_names=list(factor_category_shift_std.keys()),\n",
    "                                                factor_type=FactorType.Category)\n",
    "std_dict = {factor: factor_category_shift_std[factor.name] for factor in many_factor_categories}\n",
    "\n",
    "# Create a MacroScenario object\n",
    "scenario = MacroScenario(date, std_dict)\n",
    "\n",
    "# First, get the portfolio factor category exposure\n",
    "exposure_df = pm.get_macro_exposure(model=model, date=scenario.date, factor_type=FactorType.Category, factor_categories=many_factor_categories)\n",
    "\n",
    "# Then calculate portfolio change given std shifts in the factor categories in `scenario`\n",
    "portfolio_change = macro_stress_test(exposure_df, scenario, is_factor_category_stress=True).T.rename_axis(\"Factor Category\")\n",
    "\n",
    "# Display\n",
    "styles = [dict(selector=\"caption\", props=[(\"font-size\", \"120%\"), (\"font-weight\", \"bold\"),(\"color\", 'black')])]\n",
    "display((\n",
    "    portfolio_change.style.set_caption(\"Stress Test: Shifts in Factor Categories and their Impact on Portfolio\")\n",
    "                          .format({\"Percent Change After Stress Test\": \"{:.3f}%\", \"Exposure to Factor Category Before Stress Test\": \"${:,.0f}\", \"Exposure to Factor Category After Stress Test\": \"${:,.0f}\", \"Standard Deviation Shift\": \"{:.2f}\"}, na_rep=\"N/A\")\n",
    "                          .background_gradient().set_table_styles(styles))\n",
    ")\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Stress Test Shifts in Factors\n",
    "\n",
    "We can also stress test for shifts in individual factors and observe the impact on our portfolio. \n",
    "\n",
    "Here are the user inputs:\n",
    "* `model_id`: The model we are using\n",
    "* `portfolio_id`: Portfolio we are stress testing for.\n",
    "* `factors_shifts_std`: Map of factors and their standard deviation scenario shifts"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "from gs_quant.models.risk_model import DataAssetsRequest, RiskModelUniverseIdentifierRequest as UniverseIdentifier, FactorType\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "date = dt.date(2022, 5, 2)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "portfolio_id = \"MPE2KR4BC4RKQ5KE\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "\n",
    "factors_shift_std = {\n",
    "    \"US 2Y Infl. Expec.\": 0.5,\n",
    "    \"US 5Y Infl. Expec.\": 0.5,\n",
    "    \"US 10Y Infl. Expec.\": 0.5,\n",
    "    \"USD 10Y Real Rate\": 3.0,\n",
    "    \"JPY 10Y Real Rate\": 3.0,\n",
    "    \"EUR 10Y Real Rate\": 3.0\n",
    "}\n",
    "\n",
    "many_factors = model.get_many_factors(start_date=date, end_date=date, factor_names=list(factors_shift_std.keys()),\n",
    "                                      factor_type=FactorType.Factor)\n",
    "std_dict = {factor: factors_shift_std[factor.name] for factor in many_factors}\n",
    "\n",
    "# MacroScenario object\n",
    "scenario = MacroScenario(date, std_dict)\n",
    "\n",
    "# First, get the portfolio factor exposure\n",
    "exposure_df = pm.get_macro_exposure(model=model, date=scenario.date, factor_type=FactorType.Factor).sort_values(by=[\"Total Factor Exposure\"], axis=1, ascending=False)\n",
    "\n",
    "# Then calculate portfolio change given std shifts in the factors in `scenario`\n",
    "portfolio_change = macro_stress_test(exposure_df, scenario, is_factor_category_stress=False).T.rename_axis([\"Factor Category\", \"Factor\"])\n",
    "\n",
    "# Display\n",
    "styles = [dict(selector=\"caption\", props=[(\"font-size\", \"130%\"), (\"font-weight\", \"bold\"),(\"color\", 'black')])]\n",
    "display((\n",
    "    portfolio_change.style.set_caption(\"Stress Test: Shifts in Factors and their Impact on Portfolio\")\n",
    "                          .format({\"Percent Change After Stress Test\": \"{:.3f}%\", \"Exposure to Factor Before Stress Test\": \"${:,.0f}\", \"Exposure to Factor After Stress Test\": \"${:,.0f}\", \"Standard Deviation Shift\": \"{:.2f}\"}, na_rep=\"N/A\")\n",
    "                          .background_gradient().set_table_styles(styles))\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Which assets in the portfolio are in a Macro Regime?\n",
    "\n",
    "In a macro regime, macro factors are the most significant in explaining the movement in asset price. To determine which\n",
    "assets are in a macro regime, we look at the R Squared value, the proportion of the movement in asset price that is explained\n",
    "by the macro factors. Assets with R Squared values above 65% are in a macro regime while values below 65% are driven\n",
    "primarily by micro and idiosyncratic risk.\n",
    "\n",
    "Once an asset is in a macro regime, we can look at the fair value gap to determine if it is rich or cheap.\n",
    "The fair value gap metric is the difference between the spot price of an asset and the asset model value. An asset is\n",
    " undervalued when its fair value gap is negative while it is overvalued when it is a positive value.\n",
    "\n",
    "Given a portfolio, we can find out how many assets are in a macro regime at a specific date. We can leverage the function\n",
    "`get_fair_value_gap` and `get_r_squared` to determine the regime of each asset in the portfolio as well as its fair value\n",
    "gap. Note that the fair value gap data is available both in absolute terms (in percentage) and in standard deviation terms.\n",
    "\n",
    "Here are the user inputs:\n",
    "* `portfolio_id`: Portfolio we are using. \n",
    "* `model_id`: The model we are using to get r_squared and fair value gap data."
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, DataAssetsRequest as DataRequest, \\\n",
    "    RiskModelUniverseIdentifierRequest, Unit\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager\n",
    "from IPython.display import display\n",
    "import seaborn as sns\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "GsSession.use(Environment.PROD)\n",
    "\n",
    "# Time range\n",
    "start_date = dt.date(2022, 5, 2)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "# Model\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Portfolio and positions\n",
    "portfolio_id = \"MPE2KR4BC4RKQ5KE\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "positions = pm.get_latest_position_set().positions\n",
    "\n",
    "universe = [position.identifier for position in positions]\n",
    "universe_for_request = DataRequest(RiskModelUniverseIdentifierRequest.bbid, universe)\n",
    "\n",
    "# Get fair value gap and r squared for the assets in the portfolio\n",
    "fvg_std_df = model.get_fair_value_gap(start_date=start_date, end_date=end_date,\n",
    "                                      fair_value_gap_unit=Unit.STANDARD_DEVIATION, assets=universe_for_request)\n",
    "r_squared_df = model.get_r_squared(start_date=start_date, end_date=end_date, assets=universe_for_request)\n",
    "\n",
    "date = dt.date(2022, 5, 2).strftime(\"%Y-%m-%d\")\n",
    "fvg_one_date_df = fvg_std_df.loc[date, :].to_frame().rename(columns={date: \"Fair Value Gap\"})\n",
    "r_squared_one_date_df = r_squared_df.loc[date, :].to_frame().rename(columns={date: \"R Squared\"})\n",
    "fvg_rsq_df = r_squared_one_date_df.join(fvg_one_date_df).reset_index().round(2)\n",
    "\n",
    "# Get the Macro cheap assets\n",
    "macro_cheap_df = (\n",
    "    fvg_rsq_df[(fvg_rsq_df[\"R Squared\"] > 65) & ((fvg_rsq_df[\"Fair Value Gap\"] < 0))].set_index(\"index\")\n",
    "                                                                                     .rename_axis(\"Asset\")\n",
    "                                                                                     .sort_values(by=\"Fair Value Gap\", ascending=True)\n",
    ")\n",
    "\n",
    "# Plot Quadrant of assets that are macro cheap/rich and micro\n",
    "fig, ax = plt.subplots(figsize=(20, 15), constrained_layout=True)\n",
    "sns.scatterplot(data=fvg_rsq_df, x=\"R Squared\", y='Fair Value Gap', hue='index', legend=False)\n",
    "ax.set_title(f\"Macro Rich vs Macro Cheap Assets on {date}\")\n",
    "ax.set_xlabel(\"R Squared (Model Confidence) in %\")\n",
    "ax.set_ylabel(\"Fair Value Gap (sigma)\")\n",
    "ax.set_xlim(0, 100)\n",
    "ax.set_ylim(-3, 3)\n",
    "\n",
    "def annotate_plot(data, ax, index):\n",
    "    for i in range(data.shape[0]):\n",
    "        if data[index][i] == 'DHR UN':\n",
    "            ax.text(data[\"R Squared\"][i], data[\"Fair Value Gap\"][i], s=data[index][i], size='x-large', color='r')\n",
    "        else:\n",
    "            ax.text(data[\"R Squared\"][i], data[\"Fair Value Gap\"][i], s=data[index][i], size='large')\n",
    "\n",
    "annotate_plot(fvg_rsq_df, ax, \"index\")\n",
    "\n",
    "for pair, label in {(75, 2.2): \"Macro Rich\", (25, 2.2): \"Micro Regime\", (25, -2.2): \"Micro Regime\", (72, -2.2): \"Macro Cheap\"}.items():\n",
    "    ax.text(x=pair[0], y=pair[1], s=label, alpha=0.7, fontsize='xx-large', color='g', fontweight='bold')\n",
    "ax.axhline(y=0, color='k', linestyle='dashed')\n",
    "ax.axvline(x=65, color='k', linestyle='dashed')\n",
    "\n",
    "# Plot Just macro cheap assets\n",
    "fig, ax = plt.subplots(figsize=(18, 7), constrained_layout=True)\n",
    "sns.scatterplot(data=macro_cheap_df.reset_index(), x=\"R Squared\", y=\"Fair Value Gap\", hue=\"Asset\", legend=False)\n",
    "annotate_plot(macro_cheap_df.reset_index(), ax, \"Asset\")\n",
    "ax.set_title(f\"Macro Cheap Assets on {end_date}\")\n",
    "ax.set_ylabel(\"Fair Value Gap\")\n",
    "ax.set_xlabel(\"R Squared (in %)\")\n",
    "ax.text(x=75, y=-2.0, s=\"Macro Cheap\", alpha=0.7, fontsize='xx-large', color='g', fontweight='bold')\n",
    "\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "The first plot above classifies assets into 3 groups: macro cheap, macro rich and assets in a micro regime. The second plot shows the bottom right of the first\n",
    "plot, i.e all the assets that are macro cheap. "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "\n",
    "### Asset Sensitivity to Factors\n",
    "\n",
    "The asset Sensitivity to a macro factor is the percentage change in the asset price for 1 standard deviation move\n",
    "in that macro factor. We can leverage the function `get_universe_sensitivity` of the `MacroRiskModel` class to get a\n",
    "time series of daily asset sensitivity to macro factors for a list of assets.\n",
    "\n",
    "Here are the parameters that are needed:\n",
    "* `start_date` and `end_date`: Time range for which to get sensitivity for. \n",
    "* `model_id`: The model we are using.\n",
    "* `asset`: Asset to get macro sensitivity for.\n",
    "\n",
    "Below, we are querying a time series of daily sensitivity data over the time range for one of the macro cheap assets in the portfolio. "
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Asset Sensitivity to Factor Categories"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel, DataAssetsRequest, RiskModelUniverseIdentifierRequest as UniverseIdentifier, FactorType\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# Time range\n",
    "start_date = dt.date(2022, 5, 2)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "# The model to get sensitivity data for\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Universe\n",
    "asset = \"DHR UN\"\n",
    "universe = [asset]\n",
    "universe_for_request = DataAssetsRequest(UniverseIdentifier.bbid, universe)\n",
    "\n",
    "# Get asset sensitivity to factor categories\n",
    "factor_sens_df = model.get_universe_sensitivity(start_date=start_date, end_date=end_date, assets=universe_for_request,\n",
    "                                                factor_type=FactorType.Category, get_factors_by_name=True)\n",
    "factor_sens_df = (\n",
    "    factor_sens_df.droplevel(1)\n",
    "                  .sort_values(by=[asset], ascending=False, axis=1)\n",
    "                  .rename_axis(\"Factor Category\", axis=1)\n",
    ")\n",
    "\n",
    "# Display the dataframe\n",
    "factor_sens_to_display = factor_sens_df.T.rename(columns={asset: f\"Percent Change in Asset Price for 1 STD shift in each Factor Category for {asset}\"})\n",
    "display((\n",
    "    factor_sens_to_display.style.set_caption(f\"Sensitivity of {asset} to Factor Categories on {start_date}\")\n",
    "                          .format(\"{:.2f}%\").applymap(lambda v: \"color:red;\" if v < 0 else None)\n",
    "                          .set_table_styles([{'selector': \"caption\", 'props': [(\"font-size\", \"100%\"), (\"font-weight\", \"bold\"), (\"color\", 'black')]},\n",
    "                                             {'selector': 'table', 'props': [('width', '100px')]},\n",
    "                                             {'selector': 'thead', 'props': [(\"background-color\", \"silver\"), (\"font-size\", \"12px\"), (\"width\", \"400px\")]},\n",
    "                                             {'selector': 'tr', 'props': [(\"background-color\", \"gainsboro\"), (\"font-size\", \"12px\"), (\"border\", \"solid\"),\n",
    "                                                                       (\"border-width\", \"1px\")]},\n",
    "                                             {'selector': 'td', 'props': [(\"background-color\", \"aliceblue\"), (\"font-size\", \"12px\")]}]))\n",
    ")\n",
    "\n",
    "# Plot the portfolio percent change if every  factor category shifted 1 std\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "sns.barplot(data=factor_sens_df.T.reset_index(), x=\"Factor Category\", y=asset, ax=ax, palette=exp_palette)\n",
    "ax.set_title(f\"Expected % change in {asset} Price for 1 std Move in Factor Category on {end_date}\")\n",
    "ax.set_xlabel(\"Factor Category\")\n",
    "ax.set_ylabel(\"\")\n",
    "ax.set_yticks([], [])\n",
    "ax.tick_params(axis='x', labelrotation= 30)\n",
    "\n",
    "labls = factor_sens_df.T.reset_index()[\"Factor Category\"].values.tolist()\n",
    "values = factor_sens_df.T.reset_index()[asset].values.tolist()\n",
    "for i in range(len(labls)):\n",
    "    if values[i] > 0:\n",
    "        ax.text(i, values[i] + 0.01, s=f'{round(values[i], 1)}%', ha='center', fontsize='x-small')\n",
    "    else:\n",
    "        ax.text(i, values[i] - 0.08, s=f'{round(values[i], 1)}%', ha='center', fontsize='x-small')\n",
    "\n",
    "sns.despine(left=True)\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### Asset Sensitivity to Factors\n",
    "\n",
    "Once we know an asset sensitivity to factor categories, we can also query its sensitivity to individual macro factors within a factor category.\n",
    "\n",
    "Here are the user inputs:\n",
    "* `model`: The model we are using.\n",
    "* `factor_categories`: A list of factor categories whose factors we want sensitivity for. If empty, it will default to returning sensitivity to the top 2 positive and negative macro drivers\n",
    "* `start_date` and `end_date`: Time range to get sensitivity data for an asset"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "from gs_quant.models.risk_model import DataAssetsRequest, RiskModelUniverseIdentifierRequest as UniverseIdentifier, FactorType\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "\n",
    "# Time range\n",
    "start_date = dt.date(2022, 5, 2)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "# The model to get data for\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Select factor_categories\n",
    "factor_categories = []\n",
    "\n",
    "# Universe\n",
    "asset = \"DHR UN\"\n",
    "universe = [asset]\n",
    "universe_for_request = DataAssetsRequest(UniverseIdentifier.bbid, universe)\n",
    "factor_data = model.get_factor_data(start_date=start_date, end_date=end_date).set_index('name')\n",
    "\n",
    "# Get asset sensitivity to factors as a dataframe\n",
    "factor_sens_df = model.get_universe_sensitivity(start_date=start_date, end_date=end_date,\n",
    "                                                assets=universe_for_request, get_factors_by_name=True)\n",
    "factor_sens_df = factor_sens_df.set_axis(pd.MultiIndex.from_tuples(\n",
    "        [(factor_data.loc[x, 'factorCategory'], x) for x in factor_sens_df.columns]), axis=1).droplevel(1)\n",
    "\n",
    "if not factor_categories:\n",
    "    # Get top drivers\n",
    "    top_drivers = (\n",
    "        model.get_universe_sensitivity(start_date=start_date, end_date=end_date, assets=universe_for_request,\n",
    "                                       factor_type=FactorType.Category,\n",
    "                                       get_factors_by_name=True).droplevel(1)\n",
    "                                                                .sort_values(by=asset, axis=1, ascending=False)\n",
    "                                                                .columns.values.tolist()\n",
    "    )\n",
    "    factor_categories = top_drivers[0:2] + top_drivers[-2:None]\n",
    "\n",
    "factor_sens_df = (\n",
    "    factor_sens_df[factor_categories].T.rename_axis([\"Factor Category\", \"Factor\"])\n",
    "                                       .sort_values(by=asset, ascending=False)\n",
    "                                       .rename(columns={asset: f\"Percent Change in Asset Price for 1 STD shift in each Factor for {asset}\"})\n",
    ")\n",
    "\n",
    "# Display the dataframe\n",
    "display((\n",
    "    factor_sens_df.style.set_caption(f\"Sensitivity of {asset} to Factors on {start_date}\")\n",
    "                        .format(\"{:.2f}%\")\n",
    "                        .applymap(lambda v: \"color:red;\" if v < 0 else None)\n",
    "                        .set_table_styles([{'selector': \"caption\", 'props': [(\"font-size\", \"100%\"), (\"font-weight\", \"bold\"), (\"color\", 'black')]},\n",
    "                                           {'selector': 'table', 'props': [('width', '100px')]},\n",
    "                                           {'selector': 'thead', 'props': [(\"background-color\", \"silver\"), (\"font-size\", \"12px\"), (\"width\", \"400px\")]},\n",
    "                                           {'selector': 'tr', 'props': [(\"background-color\", \"gainsboro\"), (\"font-size\", \"12px\"), (\"border\", \"solid\"),\n",
    "                                                                       (\"border-width\", \"1px\")]},\n",
    "                                           {'selector': 'td', 'props': [(\"background-color\", \"aliceblue\"), (\"font-size\", \"12px\")]}]))\n",
    ")\n",
    "\n",
    "# Plot the portfolio percent change if every  factor category shifted 1 std\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "sns.barplot(data=factor_sens_df.reset_index(), x=\"Factor\", y=f\"Percent Change in Asset Price for 1 STD shift in each Factor for {asset}\", ax=ax, palette=exp_palette)\n",
    "ax.set_title(f\"Expected % change in {asset} Price for 1 std Move in Factor Category on {end_date}\")\n",
    "ax.set_xlabel(\"Macro Factor\")\n",
    "ax.set_ylabel(\"\")\n",
    "ax.set_yticks([], [])\n",
    "ax.tick_params(axis='x', labelrotation=60)\n",
    "\n",
    "labls = factor_sens_df.reset_index()[\"Factor\"].values.tolist()\n",
    "values = factor_sens_df.reset_index()[f\"Percent Change in Asset Price for 1 STD shift in each Factor for {asset}\"].values.tolist()\n",
    "for i in range(len(labls)):\n",
    "    if values[i] > 0:\n",
    "        ax.text(i, values[i] + 0.01, s=f'{round(values[i], 1)}%', ha='center', fontsize='x-small')\n",
    "    else:\n",
    "        ax.text(i, values[i] - 0.03, s=f'{round(values[i], 1)}%', ha='center', fontsize='x-small')\n",
    "\n",
    "sns.despine(left=True)\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Historical Asset Sensitivity to Factor Categories\n",
    "\n",
    "We can query asset sensitivity to factor categories over time. To get sensitivity to factor categories, make sure that the factor_type parameter in the function `get_universe_sensitivity` is set to `FactorType.Category`.\n",
    "\n",
    "If the factor_categories parameter is not specified, the top drivers of the asset will be shown instead.\n",
    "\n",
    "On the plot, she shaded area represents the time range when the asset was in a macro regime."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "from gs_quant.models.risk_model import DataAssetsRequest, RiskModelUniverseIdentifierRequest as UniverseIdentifier, FactorType\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "from itertools import groupby, cycle\n",
    "\n",
    "# Time range\n",
    "start_date = dt.date(2021, 1, 1)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "# The model to get data for\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Asset and factor categories\n",
    "asset = \"DHR UN\"\n",
    "factor_categories = []\n",
    "\n",
    "universe = [asset]\n",
    "universe_for_request = DataAssetsRequest(UniverseIdentifier.bbid, universe)\n",
    "\n",
    "# Get asset sensitivity to factor category (note that factor_type is set to `Category`)\n",
    "factor_category_sens_df = model.get_universe_sensitivity(start_date=start_date, end_date=end_date, assets=universe_for_request,\n",
    "                                                         factor_type=FactorType.Category, get_factors_by_name=True)\n",
    "\n",
    "# If no factor categories passed in, get the top drivers as of end_date\n",
    "if not factor_categories:\n",
    "    top_drivers = (\n",
    "        factor_category_sens_df.loc[(asset, end_date.strftime(\"%Y-%m-%d\")), :].to_frame()\n",
    "                                                                              .droplevel(1, axis=1)\n",
    "                                                                              .sort_values(by=asset, ascending=False)\n",
    "                                                                              .index.values.tolist()\n",
    "    )\n",
    "    factor_categories = top_drivers[0:2] + top_drivers[-2:None]\n",
    "factor_category_sens_df = factor_category_sens_df[factor_categories].droplevel(0)\n",
    "\n",
    "# Get date range when `asset` was in a macro regime\n",
    "r_squared_historical = (\n",
    "    model.get_r_squared(start_date=start_date, end_date=end_date,\n",
    "                        assets=DataAssetsRequest(UniverseIdentifier.bbid, [asset])).reset_index()\n",
    "                                                                                   .rename(columns={\"index\": \"Date\"})\n",
    ")\n",
    "macro_regime_dates = r_squared_historical.loc[r_squared_historical[asset] > 65].index.tolist()\n",
    "temp_list = cycle(macro_regime_dates)\n",
    "next(temp_list)\n",
    "groups = groupby(macro_regime_dates, key=lambda j: j + 1 == next(temp_list))\n",
    "macro_regime_date_range = [list(v) + [next(next(groups)[1])] for k, v in groups if k]\n",
    "\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "factor_category_sens_df.reset_index().rename(columns={\"index\": \"Date\"}).plot(ax=ax, x=\"Date\")\n",
    "ax.set_title(f\"Sensitivity of {universe[0]} to Factor Categories on {end_date} Over Time\")\n",
    "ax.set_ylabel(\"Percentage change in asset price for 1 std shift\")\n",
    "ax.axhline(y=0, linestyle='dashed')\n",
    "for date_range in macro_regime_date_range:\n",
    "    ax.axvspan(date_range[0], date_range[-1], color='aliceblue')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Historical Asset Sensitivity to Factors\n",
    "\n",
    "We can query asset sensitivity to indiviual factors within a factor category over time. The `factor_type` parameter in the function `get_universe_sensitivity` is by default set to `FactorType.Factor` to specify that we want exposure to individual factors.\n",
    "\n",
    "If the `factors` parameter is not specified, the factors to which the asset has the most exposure will be returned. On the plot, the shaded area represents a time period when the asset was in a macro regime."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "from gs_quant.models.risk_model import DataAssetsRequest, RiskModelUniverseIdentifierRequest as UniverseIdentifier, FactorType\n",
    "import datetime as dt\n",
    "import matplotlib.pyplot as plt\n",
    "from itertools import groupby, cycle\n",
    "\n",
    "# Time range\n",
    "start_date = dt.date(2021, 1, 1)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "# Macro Model\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "# Asset and factors\n",
    "asset = \"DHR UN\"\n",
    "factors = []\n",
    "\n",
    "universe = [asset]\n",
    "universe_for_request = DataAssetsRequest(UniverseIdentifier.bbid, universe)\n",
    "\n",
    "# Get asset factor sensitivity data\n",
    "factor_sens_df = model.get_universe_sensitivity(start_date=start_date, end_date=end_date,\n",
    "                                                assets=universe_for_request, get_factors_by_name=True)\n",
    "\n",
    "# If no factors passed, get the factors with the highest positive/negative sensitivity\n",
    "if not factors:\n",
    "    top_drivers = (\n",
    "        factor_sens_df.loc[(asset, end_date.strftime(\"%Y-%m-%d\")), :].to_frame()\n",
    "                                                                     .droplevel(1, axis=1)\n",
    "                                                                     .sort_values(by=asset, ascending=False)\n",
    "                                                                     .index.values.tolist()\n",
    "    )\n",
    "    factors = top_drivers[0:2] + top_drivers[-2:None]\n",
    "\n",
    "factor_sens_df = factor_sens_df[factors].droplevel(0)\n",
    "\n",
    "# Get date range where this asset was in a macro regime\n",
    "r_squared_historical = (\n",
    "    model.get_r_squared(start_date=start_date, end_date=end_date,\n",
    "                        assets=DataAssetsRequest(UniverseIdentifier.bbid, [asset])).reset_index()\n",
    "                                                                                   .rename(columns={\"index\": \"Date\"})\n",
    ")\n",
    "macro_regime_dates = r_squared_historical.loc[r_squared_historical[asset] > 65].index.tolist()\n",
    "temp_list = cycle(macro_regime_dates)\n",
    "next(temp_list)\n",
    "groups = groupby(macro_regime_dates, key=lambda j: j + 1 == next(temp_list))\n",
    "macro_regime_date_range = [list(v) + [next(next(groups)[1])] for k, v in groups if k]\n",
    "\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "factor_sens_df.reset_index().rename(columns={\"index\": \"Date\"}).plot(ax=ax, x='Date')\n",
    "ax.set_title(f\"Sensitivity of {universe[0]} to Macro Factors on {end_date} Over Time\")\n",
    "ax.set_ylabel(\"Percentage change in asset price for 1 std shift\")\n",
    "ax.axhline(y=0, linestyle='dashed')\n",
    "for date_range in macro_regime_date_range:\n",
    "    ax.axvspan(date_range[0], date_range[-1], color='aliceblue')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Regime Shift over Time\n",
    "\n",
    "In a time range, we can pinpoint points in time when macro factors have primarily explained the variance in asset price over a period of time for some assets in our portfolio. \n",
    "In other words, we can observe when R Squared for an asset was above 65% and when it was below 65%. \n",
    "\n",
    "Here are the user inputs:\n",
    "* `model_id`: The model we are using.\n",
    "* `universe`: A list of assets to get historical R Squared data for.\n",
    "* `start_date` and `end_date`: Time range to get R Squared data"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "from gs_quant.target.risk_models import RiskModelDataAssetsRequest as DataRequest, RiskModelUniverseIdentifierRequest\n",
    "from gs_quant.models.risk_model import MacroRiskModel\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "start_date = dt.date(2021, 1, 1)\n",
    "end_date = dt.date(2022, 5, 16)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "universe = [\"DHR UN\", \"AMZN UN\"]\n",
    "universe_for_request = DataRequest(RiskModelUniverseIdentifierRequest.bbid, universe)\n",
    "\n",
    "r_squared_df = model.get_r_squared(start_date=start_date, end_date=end_date, assets=universe_for_request)\n",
    "\n",
    "fig, ax = plt.subplots(constrained_layout=True)\n",
    "r_squared_df.plot(ax=ax)\n",
    "ax.set_title(f\"Regime Shift from {start_date} to {end_date}\")\n",
    "ax.set_ylabel(\"RSquared (in %)\")\n",
    "ax.axhline(y=65, linestyle='dashed')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Historical Fair Value Gap\n",
    "\n",
    "The fair value gap is the difference between the model value and asset price. Note that a negative fair value gap (model value < asset price) means that the asset is rich, while a model value > asset price means that the asset is cheap."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "from gs_quant.target.risk_models import RiskModelDataAssetsRequest as DataRequest, RiskModelUniverseIdentifierRequest\n",
    "from gs_quant.models.risk_model import MacroRiskModel, Unit\n",
    "from itertools import groupby, cycle\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "start_date = dt.date(2021, 1, 1)\n",
    "end_date = dt.date(2022, 5, 2)\n",
    "\n",
    "model_id = \"QI_US_EQUITY_LT\"\n",
    "model = MacroRiskModel.get(model_id)\n",
    "\n",
    "asset_name = \"DHR UN\"\n",
    "universe = [asset_name]\n",
    "universe_for_request = DataRequest(RiskModelUniverseIdentifierRequest.bbid, universe)\n",
    "\n",
    "fair_value_gap_std_df = model.get_fair_value_gap(start_date=start_date, end_date=end_date, \n",
    "                                                 fair_value_gap_unit=Unit.STANDARD_DEVIATION, \n",
    "                                                 assets=universe_for_request)\n",
    "\n",
    "# Get date range where this asset was in a macro regime\n",
    "r_squared_historical = (\n",
    "    model.get_r_squared(start_date=start_date, end_date=end_date, \n",
    "                        assets=DataAssetsRequest(UniverseIdentifier.bbid, universe)).reset_index()\n",
    "                                                                                    .rename(columns={\"index\": \"Date\"})\n",
    ")\n",
    "macro_regime_dates = r_squared_historical.loc[r_squared_historical[asset_name] > 65].index.tolist()\n",
    "temp_list = cycle(macro_regime_dates)\n",
    "next(temp_list)\n",
    "groups = groupby(macro_regime_dates, key=lambda j: j + 1 == next(temp_list))\n",
    "macro_regime_date_range = [list(v) + [next(next(groups)[1])] for k, v in groups if k]\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(15, 7), dpi=150, constrained_layout=True)\n",
    "fair_value_gap_std_df.reset_index().rename(columns={\"index\": \"Date\"}).iloc[:, 0:4].plot(ax=ax, x='Date')\n",
    "ax.set_title(f\"Fair Value Gap from {start_date} to {end_date}\", fontsize='x-large', fontweight='bold')\n",
    "ax.set_ylabel(\"Fair Value Gap (sigma)\", fontsize='large')\n",
    "ax.axhline(y=0, linestyle='dashed')\n",
    "\n",
    "for date_range in macro_regime_date_range:\n",
    "    ax.axvspan(date_range[0], date_range[-1], color='aliceblue')\n",
    "plt.show()\n",
    "\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}